<?php
/**
 * @package EPass
 * @author Oliver Leisalu
 */
class Epass {
	
	private $id;
	
	private $privateKey;
	
	private $password;
	
	private $requestKey;
	
	private $callbackUrl;
	
	private $serverUrl;
	
	private $configFilePath;
	
	private function __construct( $configFilePath = NULL ) {
		$this->configFilePath = $configFilePath ? $configFilePath : DOM_CONF_DIR_PATH . 'epass.conf.php';
		
		if( !file_exists( $this->configFilePath ) )
			trigger_error('Config file is not found. Expected path: <strong>'.$this->configFilePath.'</strong>', E_USER_ERROR );
			
		require( $this->configFilePath );
		
		if( !isset($EPASS) )
			trigger_error('Epass configuration doesn\'t exist!', E_USER_ERROR);
		
		foreach( $EPASS as $key => $value  ) 
			$this->$key = $value;
	}
	
	/**
	 * Directs user to epass login page.
	 *
	 * @param String $configFilePath Optional. If not added conf file is taken from conf files folder.
	 */
	public static function sendToEpass( $configFilePath = NULL ) {
		$epass = new Epass( $configFilePath );
		$epass->_sendToEpass();
	}
	
	/**
	 * @return EpassResponse
	 * @param String $configFilePath Optional. If not added conf file is taken from conf files folder.
	 */
	public static function handleReplay( $configFilePath = NULL ) {
		$epass = new Epass( $configFilePath );
		return $epass->_handleReplay();
	}
	
	
	
	
	
	private function _sendToEpass() {
		$VK_VERSION  = '008';
		$VK_SND_ID   = $this->id;
		$VK_DATETIME = $this->gen_date();
		$VK_RETURN   = $this->callbackUrl;
		$VK_LANG     = 'et';
		$VK_STAMP    = $this->gen_stamp ();
		$_SESSION['epass_stamp'] = $VK_STAMP;

		$VK_SERVICE  = 'ET10';
		$data = $this->v008_str( array($VK_SERVICE, $VK_VERSION, $VK_SND_ID, $VK_STAMP, $VK_RETURN, $VK_DATETIME, $VK_LANG) );
		$VK_MAC = $this->sign ($data, $this->privateKey, $this->password );
		
		echo '
			<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
			<html xmlns="http://www.w3.org/1999/xhtml">
			<head>
			<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
			<title>Suunamine</title>
			</head>
			<body>
			<form id="form" action="'.$this->serverUrl.'" method="POST">
				<input type="hidden" name="VK_SERVICE"  value="'.$VK_SERVICE.'" size=4>
				<input type="hidden" name="VK_VERSION"  value="'.$VK_VERSION.'" size=3>
				<input type="hidden" name="VK_SND_ID"   value="'.$VK_SND_ID.'" size=10>
				<input type="hidden" name="VK_STAMP"    value="'. $VK_STAMP.'" size=30>
				<input type="hidden" name="VK_RETURN"   value="'.$VK_RETURN.'" size=60>
				<input type="hidden" name="VK_DATETIME" value="'.$VK_DATETIME.'" size=19>
				<input type="hidden" name="VK_LANG"     value="'.$VK_LANG.'" size=2>
				<input type="hidden" name="VK_MAC"      value="'.$VK_MAC.'" size=300>
				<input type="submit" value="Edasi epass-i"/>
			</form>
			<script type="text/javascript">
			t = setTimeout( "document.getElementById(\"form\").submit()" ,500 );
			</script>
			
			</body>
			</html>
		';
		die();
	}
	
	/**
	 * @return EpassResponse
	 */
	private function _handleReplay() {
		$data = $this->v008_str (
		    array (
		        $_POST['VK_SERVICE'],
		        $_POST['VK_VERSION'],
		        $_POST['VK_SND_ID'],
		        $_POST['VK_STAMP'],
		        $_POST['VK_NONCE'],
		        $_POST['VK_IPASS_SERVICES'],
		        $_POST['VK_USERNAME'],
		        $_POST['VK_NAME'],
		        $_POST['VK_DATETIME'],
		        $_POST['VK_LANG']
		    )
		);
		
		if ( !$this->verify($data, $_POST['VK_MAC'], $this->requestKey) ) {
			return new EpassResponse(1);
		}
		
		if ( $_SESSION['epass_stamp'] != $_POST ['VK_STAMP']) {
			return new EpassResponse(2);	
			unset ($_SESSION['epass_stamp']);
		}

		return new EpassResponse(0, $_POST['VK_USERNAME'], $_POST['VK_NAME']);
	}
	

	
	# data - data to be checked
	# signature - base64 encoded signature
	# pem - pem formatted pubkey file
	# returns: TRUE if verified, FALSE on error
	private function verify ($data, $signature, $pem) {
		if (($key = $this->read_pem ($pem)) == FALSE) {
			return FALSE;
		}
		$ck = openssl_get_publickey ($key);
		$sign = base64_decode ($signature);
		if (openssl_verify ($data, $sign, $ck) == 1) {
			return TRUE;
		} else {
			return FALSE;
		}
	}

	# data - password to be decrypted
	# key - pem formatted private key
	# returns: plaintext password or FALSE on error
	private function decrypt_pw($data,$key,$passphrase) {
		$fp=fopen($key,"r");
		$priv_key=fread($fp,8192);
		fclose($fp);
		$data = base64_decode($data);
		$res = openssl_get_privatekey($priv_key,$passphrase);
		if (openssl_private_decrypt($data,$decoded,$res))
			return $decoded;
		else
			return FALSE;
	}

	# data - data to be signed
	# pem - pem formatted private key
	# returns: base64 encoded signature or FALSE on error
	private function sign ($data, $pem, $passph = "") {
		if ($passph == "") {
			$pk = openssl_get_privatekey ("file://$pem");
		} else {
			$pk = openssl_get_privatekey ("file://$pem", $passph);
		}
		if (openssl_sign ($data, $signature, $pk)) {
			return base64_encode ($signature);
		} else {
			return FALSE;
		}
	}

	# pem - pem formatted keyfile
	# returns: key or FALSE on error
	private function read_pem ($pem) {
		if ($fp = fopen ($pem, "r")) {
			$key = fread ($fp, filesize ($pem));
			fclose ($fp);
			return $key;
		} else {
			return FALSE;
		}
	}

	# creates a ver 008 formatted string
	# from ordered array
	private function v008_str ($vk) {
		$out = "";
		foreach ($vk as $s) {
			$s1 = stripslashes(utf8_decode($s));
			$out .= sprintf ("%03d%s", strlen ($s1), stripslashes($s));
		}
		return $out;
	}

	# generate random STAMP
	private function gen_stamp () {
		return substr (md5 (microtime () . rand (11111111, 99999999) . time ()), 0, 30);
	}

	# generate datestring, if $t == "", use current timestamp
	private function gen_date ($t = "") {
		if ($t == "") { $t = time (); }
		return date ("d.m.Y H:i:s", $t);
	}

	# checks if DATETIME is correctly formated, and returns
	# unix timestamp on sucess. -1 on error.
	private function chk_date ($d) {
		$k = sscanf ($d, "%d.%d.%d %d:%d:%d");
		$t = mktime ($k[3], $k[4], $k[5], $k[1], $k[0], $k[2]);
		$dd = $this->gen_date ($t);
		if ($d == $dd) {
			return $t;
		} else {
			return -1;
		}
	}
	
	
}
?>